{
  "cells": [
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "view-in-github"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/stanfordnmbl/opencap-processing/blob/main/example.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "6yZsqdgGRvXV"
      },
      "source": [
        "# Getting started with analyzing OpenCap data using OpenSim API\n",
        "\n",
        "The most recent release of OpenSim 4.4 enables users to import OpenSim into a colab notebook using Conda. This allows users to interact with the OpenSim API to perform more complex analysis in addition to simple visualization.\n",
        "\n",
        "To learn more about using the [OpenSim Conda package](https://anaconda.org/opensim-org/opensim), please refer to these tutorials in google colab: https://github.com/opensim-org/opensim-models/tree/master/notebooks/tutorials\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "jPuIlRVNRRDf"
      },
      "source": [
        "# Install OpenSim using condacolab\n",
        "\n",
        "The first step is to install [condacolab](https://github.com/conda-incubator/condacolab) in your notebook. Once is this complete, you can use conda to install the OpenSim Conda package into the enviroment. \n",
        "\n",
        "Note, this might take a few minutes to complete."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QR8biTxkIEmC"
      },
      "outputs": [],
      "source": [
        "!pip install -q condacolab\n",
        "import condacolab\n",
        "condacolab.install()\n",
        "!conda install -c opensim_admin opensim\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "OAdQ51I2TUZ8"
      },
      "source": [
        "# Import the OpenCap processing source code from the GitHub repo\n",
        "\n",
        "The source code for this example can be found on GitHub: https://github.com/stanfordnmbl/opencap-processing\n",
        "\n",
        "Note, there are instructions in the readme of the repo with the necessary conda commands to run the example code locally on your computer via the command prompt. There are also instructions to perform muscle driven simulations and for downloading your data from OpenCap using Python.\n",
        "\n",
        "Run the next section of code to import the GitHub repository and install the necessary Python packages.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Z6laCH4LMpu3"
      },
      "outputs": [],
      "source": [
        "!git clone https://github.com/stanfordnmbl/opencap-processing.git\n",
        "%cd /content/opencap-processing\n",
        "!python3 -m pip install -r requirements.txt"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "b7uaKDZ8TObE"
      },
      "source": [
        "If the block above runs successfully, you should see that it installed several Python packages with a message:\n",
        "\n",
        "```\n",
        "Successfully installed backcall-0.2.0 cycler-0.11.0 decorator-5.1.1 evdev-1.6.0 fonttools-4.37.4 ipython-7.34.0 jedi-0.18.1 kiwisolver-1.4.4 maskpass-0.3.6 matplotlib-3.5.3 matplotlib-inline-0.1.6 packaging-21.3 pandas-1.3.5 parso-0.8.3 pexpect-4.8.0 pickleshare-0.7.5 pillow-9.2.0 prompt-toolkit-3.0.31 ptyprocess-0.7.0 pygments-2.13.0 pynput-1.7.6 pyparsing-3.0.9 python-dateutil-2.8.2 python-decouple-3.6 python-xlib-0.31 pytz-2022.4 scipy-1.7.3 traitlets-5.4.0 typing-extensions-4.3.0 wcwidth-0.2.5.\n",
        "```"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "kFO8l3_jUS_v"
      },
      "source": [
        "# Import Packages\n",
        "\n",
        "Now that the correct Python packages have been installed and the source code cloned to this note book, you can import packages specific to the source code (`utilsKinematics`, `utils`, and `utilsPlotting`).\n",
        "\n",
        "Note, you will be prompted to login to OpenCap with your account. If you have not created an account, you can visit [opencap.ai](https://opencap.ai) and click \"Start Recording\" to create an account. This will enable you to authenticate and analyze your own data in the future."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Z8-FwOx1hJUr"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "import utilsKinematics\n",
        "from utils import download_kinematics\n",
        "from utilsPlotting import plot_dataframe\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "CI0NEHcNWVbg"
      },
      "source": [
        "You should see a `Login successful` message. Note, you will receive a two factor authentication email from noreply@opencap.ai. You do not need to enter the code to perform these analyses."
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "bKHFrwpgVYGW"
      },
      "source": [
        "# Example processing stream for analysis\n",
        "\n",
        "This example will walk through importing data, obtaining parameters through the OpenSim API, plotting, and saving parameters for analysis. \n",
        "\n",
        "First, enter the session id of your OpenCap data collection. This will be used to download the data directly from OpenCap. Note, you will have needed to authenticate your account in the previous step for this to work properly.\n",
        "\n",
        "If you have not collected data yet, we have provided a `session_id` of a public example that can be used for the rest of the tutorial.\n",
        "\n",
        "Run the next block of code to download the data.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "id": "aPPEw6-og6qY"
      },
      "outputs": [],
      "source": [
        "\n",
        "\n",
        "# User inputs.\n",
        "# Specify session id; see end of url in app.opencap.ai/session/<session_id>.\n",
        "session_id = \"4d5c3eb1-1a59-4ea1-9178-d3634610561c\" # Public example session_id is \"4d5c3eb1-1a59-4ea1-9178-d3634610561c\"\n",
        "\n",
        "# Specify trial names in a list; use None to process all trials in a session.\n",
        "specific_trial_names = ['walk_1_25ms', 'run_2_5ms'] # There are more trials in the session, let's look at these two only for now.\n",
        "\n",
        "# Specify where to download the data.\n",
        "data_folder = os.path.join(\"./Data\", session_id)\n",
        "\n",
        "# Download data.\n",
        "trial_names, modelName = download_kinematics(session_id, folder=data_folder, trialNames=specific_trial_names)"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "zEs8qPkU8SbM"
      },
      "source": [
        "# Process the data using OpenSim\n",
        "\n",
        "Now that your data has been downloaded, you can extract kinematic parameters using OpenSim. For more details of the step by step process, please refer to the `utilsKinematics.py` code in the github repository.\n",
        "\n",
        "For this example, we analyze trials of a person walking at 1.25 m/s and running at 2.5 m/s. For each of the trials, the code below calculates the following:\n",
        "1.  coordinate values, speeds, and accelerations\n",
        "2.  muscle-tendon lengths\n",
        "3.  center of mass values, speeds, and accelerations.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "id": "pGAZfsaN8HDC"
      },
      "outputs": [],
      "source": [
        "# Process data.\n",
        "kinematics, coordinates, muscle_tendon_lengths, moment_arms, center_of_mass = {}, {}, {}, {}, {}\n",
        "coordinates['values'], coordinates['speeds'], coordinates['accelerations'] = {}, {}, {}\n",
        "center_of_mass['values'], center_of_mass['speeds'], center_of_mass['accelerations'] = {}, {}, {}\n",
        "\n",
        "for trial_name in trial_names:\n",
        "    # Create object from class kinematics.\n",
        "    kinematics[trial_name] = utilsKinematics.kinematics(data_folder, trial_name, modelName=modelName, lowpass_cutoff_frequency_for_coordinate_values=10)\n",
        "    \n",
        "    # Get coordinate values, speeds, and accelerations.\n",
        "    coordinates['values'][trial_name] = kinematics[trial_name].get_coordinate_values(in_degrees=True) # already filtered\n",
        "    coordinates['speeds'][trial_name] = kinematics[trial_name].get_coordinate_speeds(in_degrees=True, lowpass_cutoff_frequency=10)\n",
        "    coordinates['accelerations'][trial_name] = kinematics[trial_name].get_coordinate_accelerations(in_degrees=True, lowpass_cutoff_frequency=10)\n",
        "    \n",
        "    # Get muscle-tendon lengths.\n",
        "    muscle_tendon_lengths[trial_name] = kinematics[trial_name].get_muscle_tendon_lengths()\n",
        "    \n",
        "    # Get center of mass values, speeds, and accelerations.\n",
        "    center_of_mass['values'][trial_name] = kinematics[trial_name].get_center_of_mass_values(lowpass_cutoff_frequency=10)\n",
        "    center_of_mass['speeds'][trial_name] = kinematics[trial_name].get_center_of_mass_speeds(lowpass_cutoff_frequency=10)\n",
        "    center_of_mass['accelerations'][trial_name] = kinematics[trial_name].get_center_of_mass_accelerations(lowpass_cutoff_frequency=10)\n",
        "    "
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "Q2pH7x889QfF"
      },
      "source": [
        "# Plotting\n",
        "\n",
        "Now that these values are calculated, you can visualize them. For example, if you were interested in the **difference in hip and knee angular velocity during walking and running**, you can plot them together.\n",
        "\n",
        "Run the next block of code to see the plots. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "om6FQgkT8NR_"
      },
      "outputs": [],
      "source": [
        "# Plot selected coordinate speeds against time.\n",
        "plot_dataframe(dataframes = [coordinates['speeds'][trial_names[0]], \n",
        "                             coordinates['speeds'][trial_names[1]]],\n",
        "               y = ['hip_flexion_l', 'knee_angle_l'],\n",
        "               xlabel = 'Time (s)',\n",
        "               ylabel = 'Angular Velocity (deg/s)',\n",
        "               title = 'Joint Velocity',\n",
        "               labels = [trial_names[0], trial_names[1]], xrange=[0,3.5])"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "ZHDOFI3SCLvS"
      },
      "source": [
        "As you might expect, both **hip and knee angular velocity are larger during running compared to walking**. In addition to large hip and knee angular velocity, you might also expect the center of mass accelerations to increase during running as seen in next plots."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "TNHQOsEiCHaN"
      },
      "outputs": [],
      "source": [
        "# Plot center of mass accelerations.\n",
        "plot_dataframe(dataframes = [center_of_mass['accelerations'][trial_names[0]], \n",
        "                             center_of_mass['accelerations'][trial_names[1]]],\n",
        "               xlabel = 'Time (s)',\n",
        "               ylabel = 'Acceleration (m/${s^2}$)',\n",
        "               title = 'Center of mass accelerations',\n",
        "               labels = [trial_names[0], trial_names[1]], xrange=[0,3.5])\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "rjc2dBi_C6KG"
      },
      "source": [
        "In addition to kinematic parameters, you might also expect **muscle tendon lengths to change during running vs walking**. These values were calculated using OpenSim above.\n",
        "\n",
        "Run the next block of code to plot the muscle tendon lengths of the biceps femoris, medial gastrocnemius, and rectus femoris."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4J--sIcZCHIg"
      },
      "outputs": [],
      "source": [
        "# Plot muscle-tendon lengths against time.\n",
        "plot_dataframe(dataframes = [muscle_tendon_lengths[trial_names[0]], \n",
        "                             muscle_tendon_lengths[trial_names[1]]],\n",
        "               y = ['bflh_r', 'gasmed_r', 'recfem_r'],\n",
        "               xlabel = 'Time (s)',\n",
        "               ylabel = 'Length (m)',\n",
        "               title = 'Muscle-tendon lengths',\n",
        "               labels = [trial_names[0], trial_names[1]], xrange=[0,3.5])"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "aSWHqbDi9ncg"
      },
      "source": [
        "# Download data to a CSV file\n",
        "\n",
        "Now that your processing is complete, you can export the data to a CSV file for futher processing.\n",
        "\n",
        "The next block of code creates the path and stores the coordinate speeds in a CSV file."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kY2C9Afz8G3z"
      },
      "outputs": [],
      "source": [
        "# Print as csv: example.\n",
        "output_csv_dir = os.path.join(data_folder, 'OpenSimData', 'Kinematics', 'Outputs') # This creates the following path for the data to be stored /content/opencap-processing/Data/4d5c3eb1-1a59-4ea1-9178-d3634610561c/OpenSimData/Kinematics/Outputs/\n",
        "os.makedirs(output_csv_dir, exist_ok=True)\n",
        "output_csv_path = os.path.join(output_csv_dir, 'coordinate_speeds_{}.csv'.format(trial_names[0]))\n",
        "coordinates['speeds'][trial_names[0]].to_csv(output_csv_path)"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "eI0VXnzO_DIQ"
      },
      "source": [
        "The path to the file is: /content/opencap-processing/Data/4d5c3eb1-1a59-4ea1-9178-d3634610561c/OpenSimData/Kinematics/Outputs/coordinate_speeds_walk_1_25ms.csv\n",
        "\n",
        "Use the file browser in the left panel to navigate to the file and inspect. The output CSV gives you coordinate speeds at each time frame. \n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "gJDXdPN4-xl7"
      },
      "source": [
        "![Screen Shot 2022-10-04 at 12.00.18 PM.png]()"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "5-WaXB8c_SoK"
      },
      "source": [
        "# Other Resources\n",
        "> **OpenSim Website:** https://opensim.stanford.edu/\n",
        ">\n",
        "> **OpenSim API Documentation:** https://simtk.org/api_docs/opensim/api_docs/\n",
        "> \n",
        "> **SimTK Website:** https://simtk.org/projects/opensim\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "metadata": {
        "id": "MNLRx4_O9-ld"
      },
      "source": [
        "Version 1.05\n",
        "\n",
        "Creators: Antoine Falisse, Scott Uhlrich, Matt Petrucci.\n",
        "Last Updated on September 21, 2022\n",
        "\n",
        "This notebook is made available under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0)."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "include_colab_link": true,
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
